package org.sean.generator.service;

import cn.hutool.core.io.IoUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.text.WordUtils;
import org.sean.framework.util.StringUtil;
import org.sean.framework.web.service.FreeMarkerService;
import org.sean.generator.entity.ColumnEntity;
import org.sean.generator.entity.TableEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 生成代码
 */
@Service
public class CodeService {
    @Autowired
    private FreeMarkerService freeMarkerService;

    public List<String> getTemplates(boolean isFrameCode) {
        List<String> templates = new ArrayList<>();
        // framework code
        if (isFrameCode) {
            templates.add("ROOT_DIR/.gitignore.vm");
            templates.add("ROOT_DIR/pom.xml.vm");
            templates.add("ROOT_DIR/beans/pom.xml.vm");
            templates.add("ROOT_DIR/client/pom.xml.vm");
            templates.add("ROOT_DIR/client/src/test/java/package/client/module/BootApplication.java.vm");
            templates.add("ROOT_DIR/client/src/test/java/package/client/module/BaseTest.java.vm");
            templates.add("ROOT_DIR/client/src/test/resources/application.yml.vm");
            templates.add("ROOT_DIR/client/src/test/resources/bootstrap.yml.vm");
            templates.add("ROOT_DIR/server/pom.xml.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/ApplicationLauncher.java.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/module/entity/MultiTable.java.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/module/mapper/MultiMapper.java.vm");
            templates.add("ROOT_DIR/server/src/main/resources/package/module/mapper/MultiMapper.xml.vm");
            templates.add("ROOT_DIR/server/src/main/resources/bootstrap.yml.vm");
            templates.add("ROOT_DIR/server/src/test/java/package/module/BootApplication.java.vm");
        } else {
            // biz code
            // beans
            templates.add("ROOT_DIR/beans/src/main/java/package/module/api/Api.java.vm");
            templates.add("ROOT_DIR/beans/src/main/java/package/module/dto/DTO.java.vm");
            templates.add("ROOT_DIR/beans/src/main/java/package/module/dto/Query.java.vm");
            // client
            templates.add("ROOT_DIR/client/src/main/java/package/client/module/Client.java.vm");
            templates.add("ROOT_DIR/client/src/main/java/package/client/module/FallbackFactory.java.vm");
            templates.add("ROOT_DIR/client/src/test/resources/contracts/RequestContract.groovy.vm");
            templates.add("ROOT_DIR/client/src/test/java/package/client/module/ContractTest.java.vm");
            // server
            templates.add("ROOT_DIR/server/src/main/java/package/module/entity/Entity.java.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/module/mapper/EntityMapper.java.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/module/controller/Controller.java.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/module/manager/Manager.java.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/module/service/Service.java.vm");
            templates.add("ROOT_DIR/server/src/main/java/package/module/service/impl/ServiceImpl.java.vm");
            templates.add("ROOT_DIR/server/src/test/java/package/module/ControllerTest.java.vm");
        }
        return templates;
    }

    /**
     * 获取文件名
     */
    public String getFileName(String template, String groupId, String artifactId, String className) {
        if (StringUtil.isBlank(className)) {
            className = "";
        }
        return template.replace("ROOT_DIR", artifactId)
                .replace("package", getPackagePath(groupId))
                .replace("module", getModulePath(artifactId))
                .replaceFirst("beans", String.format("%s-beans", artifactId))
                .replaceFirst("client", String.format("%s-client", artifactId))
                .replaceFirst("server", String.format("%s-server", artifactId))
                .replaceFirst("Controller", String.format("%sController", className))
                .replaceFirst("Manager", String.format("%sManager", className))
                .replaceFirst("Service", String.format("%sService", className))
                .replaceFirst("EntityMapper", String.format("%sMapper", className))
                .replaceFirst("Entity", String.format("%sEntity", className))
                .replaceFirst("Api", String.format("%sApi", className))
                .replaceFirst("DTO", String.format("%s", className))
                .replaceFirst("Query", String.format("%sQuery", className))
                .replaceFirst("Client", String.format("%sClient", className))
                .replaceFirst("ContractTest", String.format("%sContractTest", className))
                .replaceFirst("RequestContract", String.format("%sContract", className))
                .replaceFirst("FallbackFactory", String.format("%sFallbackFactory", className))
                .replaceFirst("MultiTable", String.format("MultiEntity", className))
                .replaceAll("\\.vm", "");
    }

    /**
     * 生成代码
     */
    public void generatorCode(String tablePrefix, String version, String groupId, String artifactId, Map<String, String> table,
                              List<Map<String, String>> columns, ZipOutputStream zip) {
        //封装模板数据
        Map<String, Object> map = new HashMap<>();
        map.put("version", getConfig("version"));
        if (StringUtil.isEmpty(version)) {
            map.put("framework_version", getConfig("framework_version"));
        } else {
            map.put("framework_version", version);
        }
        String ignoreFields = getConfig("ignore_fields");
        if (StringUtil.isNotEmpty(ignoreFields)) {
            map.put("ignoreFields", ignoreFields.split(","));
        }
        // frame
        map.put("APPLICATION_NAME", "${APPLICATION_NAME:" + artifactId + "}");
        map.put("NACOS_URI", getConfig("NACOS_URI"));
        map.put("NACOS_EXT", getConfig("NACOS_EXT"));
        map.put("NACOS_NAMESPACE", getConfig("NACOS_NAMESPACE"));
        map.put("NACOS_GROUP", getConfig("NACOS_GROUP"));

        map.put("artifactId", artifactId);
        map.put("groupId", groupId);

        map.put("package", getPackageName(groupId));
        map.put("module", getModuleName(artifactId));

        map.put("author", getConfig("author"));
        map.put("date", DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd"));

        String className = null;

        boolean isFrameCode = ObjectUtils.isEmpty(table);

        if (!isFrameCode) {
            boolean hasBigDecimal = false;
            boolean hasDate = false;
            //表信息
            TableEntity tableEntity = new TableEntity();
            tableEntity.setTableName(table.get("tableName"));
            tableEntity.setComments(table.get("tableComment"));
            //表名转换成Java类名
            className = tableToJava(tableEntity.getTableName(), tablePrefix);
            tableEntity.setClassName(className);
            tableEntity.setClassname(StringUtils.uncapitalize(className));

            //列信息
            List<ColumnEntity> columsList = new ArrayList<>();
            for (Map<String, String> column : columns) {
                ColumnEntity columnEntity = new ColumnEntity();
                columnEntity.setColumnName(column.get("columnName"));
                columnEntity.setDataType(column.get("dataType"));
                columnEntity.setComments(column.get("columnComment"));
                columnEntity.setExtra(column.get("extra"));

                //列名转换成Java属性名
                String attrName = columnToJava(columnEntity.getColumnName());
                columnEntity.setAttrName(attrName);
                columnEntity.setAttrname(StringUtils.uncapitalize(attrName));

                //列的数据类型，转换成Java类型
                String attrType = getConfig(columnEntity.getDataType(), "unknowType");
                columnEntity.setAttrType(attrType);
                if (!hasBigDecimal && "BigDecimal".equals(attrType)) {
                    hasBigDecimal = true;
                }
                if (!hasDate && "Date".equals(attrType)) {
                    hasDate = true;
                }
                //是否主键
                if ("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() == null) {
                    tableEntity.setPk(columnEntity);
                }

                columsList.add(columnEntity);
            }
            tableEntity.setColumns(columsList);

            //没主键，则第一个字段为主键
            if (tableEntity.getPk() == null) {
                tableEntity.setPk(tableEntity.getColumns().get(0));
            }

            map.put("tableName", tableEntity.getTableName());
            map.put("comments", tableEntity.getComments());
            map.put("pk", tableEntity.getPk());
            map.put("className", tableEntity.getClassName());
            map.put("classname", tableEntity.getClassname());
            map.put("pathName", tableEntity.getClassname().toLowerCase());
            map.put("columns", tableEntity.getColumns());
            map.put("hasBigDecimal", hasBigDecimal);
            map.put("hasDate", hasDate);
        }


        //获取模板列表
        List<String> templates = getTemplates(isFrameCode);
        addFile(artifactId, groupId, zip, className, map, templates);
    }

    private void addFile(String artifactId, String groupId, ZipOutputStream zip, String className,
                         Map<String, Object> map, List<String> templates) {
        for (String template : templates) {
            try {
                String content = freeMarkerService.getTemplateContent(template, map);
                //添加到zip
                zip.putNextEntry(new ZipEntry(getFileName(template, groupId, artifactId, className)));
                IoUtil.write(zip, "UTF-8", false, content);
                zip.closeEntry();
            } catch (Exception e) {
                throw new RuntimeException("渲染模板失败，文件：" + template, e);
            }
        }
    }

    private String getModuleName(String artifactId) {
        return artifactId.replace("-", "_").toLowerCase();
    }

    private String getModulePath(String artifactId) {
        return getModuleName(artifactId).replace(".", File.separator);
    }

    private String getPackageName(String groupId) {
        return groupId.replace("-", "_").toLowerCase();
    }

    private String getPackagePath(String groupId) {
        return getPackageName(groupId).replace(".", File.separator);
    }

    /**
     * 列名转换成Java属性名
     */
    public String columnToJava(String columnName) {
        return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
    }

    /**
     * 表名转换成Java类名
     */
    public String tableToJava(String tableName, String tablePrefix) {
        if (StringUtils.isNotBlank(tablePrefix)) {
            tableName = tableName.replace(tablePrefix, "");
        }
        return columnToJava(tableName);
    }

    /**
     * 获取配置信息
     */
    public String getConfig(String key) {
        return getConfig(key, null);
    }

    public String getConfig(String key, String defValue) {
        try {
            // 获得资源文件
            ResourceBundle rb = ResourceBundle.getBundle("generator");
            // 获得相应的key值
            return rb.getString(key);
        } catch (Exception e) {
            return defValue;
        }
    }

}
